
def randomly_moving_dots_search_settings():
    '''
    This function tells you what settings are needed and in what order for this stimulus.
    Inputs:
        None
    Returns:
        settings        : A list of tupples.  In each tupple is
                            (setting_name, setting_description, default_value).
                          The list is of length num_settings + 1, so that the final
                             value in the list is True/False wether or not the stimulus
                             can be triggered via the parallel port or not.
    '''
    settings = []
    settings.append( ('dot_sizes',
                     'The size of the moving dots. (normalized RF units) [list]',
                     [0.16, 0.16, 0.16, 0.22, 0.22, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16] ))
    settings.append( ('speeds',
                     'How fast the dots move. (normalized RF units per second) [list]',
                     [0.25, 0.50] ))
    settings.append( ('duration',
                     'How long the motion will last. (seconds)',
                     20.0 ))
    settings.append( ('shade',
                     '1.0 for black dots, -1.0 for white dots.',
                     1.0 ))
    settings.append( ('win_size',
                     'The dimensions of the stimulus delivery window. (pixels) [list]',
                     [1440,900] ))
    settings.append( ('refresh_rate',
                     'The number of screen refreshes per second. (Hz)',
                     60 ))
    settings.append( ('random_seed',
                     'The number to seed the random number generator.',
                     40 ))
    settings.append(True) # this stimulus can be triggered
    return settings


def find_x_y(start_pos, end_pos, speed, refresh_rate):
    """
    Calculates the trajectory of a moving object.
    Inputs:
        start_pos
        end_pos
        speed
        refresh_rate
    Returns:
        positions
    """
    import scipy
    start_pos = scipy.array(start_pos)
    end_pos = scipy.array(end_pos)
    distance = scipy.sqrt( (start_pos[0]-end_pos[0])**2 + 
                           (start_pos[1]-end_pos[1])**2   )
    # how many frames we'll display
    num_frames = int( (distance/speed)*refresh_rate )
    #print "Started at: %s and moving to: %s" % (str(start_pos), str(end_pos))
    #print "distance we're going to move is: %f" % distance
    #print "speed = %f" % speed
    #print "num_frames = %d" % num_frames
    # how many frames we'll display
    # how the dot should move each frame
    dx         = (end_pos[0]-start_pos[0])/(num_frames*1.0)
    dy         = (end_pos[1]-start_pos[1])/(num_frames*1.0)
    positions = [(start_pos[0]+dx*i, start_pos[1]+dy*i) for i in xrange(num_frames)]
    positions[-1] = (end_pos[0], end_pos[1])

    return positions

def randomly_moving_dots_search_runtime(dot_sizes, speeds, duration, shade, 
                         win_size, refresh_rate, random_seed): 
    """
    Calculate and return the number of seconds that this stimulus will need to run, after being triggered.
    """
    return duration + 0.99 # 0.99 added so rounding is always up.

def randomly_moving_dots_search(dot_sizes, speeds, duration, shade, 
                         win_size, refresh_rate, random_seed, 
                         window=None,
                         eye_cup=None, 
                         rf=None, 
                         frame_info=None, 
                         preview=True):
    """
    This stimulus is a set of dots of various sizes with various speeds moving around in a
        square the size of the receptive field.
    Inputs:
        dot_sizes       : a list of the sizes of the dots in normalized units
        speeds          : a list of the speeds of the dots in normalized units per second
        duration        : how long the movement will be, in seconds.
        shade           : 1.0 for black dots, -1.0 for white dots
        win_size        : dimensions of the screen in pixels, (1440,900) is default
        refresh_rate    : refresh rate in Hz of the video, 60.0 is default.
        random_seed     : the seed for the random number generator.
        --- kwargs --
        window          : a psychopy.visual Window object, if not passed it will be constructed.
                        :    if passed it will not be closed (a blank screen will be drawn)
        eye_cup         : An Eye_cup object so we can draw the eye_cup on the screen and
                             set up the normalized coordinate system (size of eye_cup)
        rf              : an RF object so we can set up the normalized coordinate system
        frame_info      : if None, then no frames are saved, otherwise it is a 
                                dictionary with 
                                    'factor':float 
                                so we can scale down the frames and
                                output a frames.gif (an animated gif file)
                                and 
                                    'times':list
                                so we know how many frames to render.
        preview         : if True then the frames will display immediately,
                             otherwise we will wait for a pulse on the parallel port.
    Returns:
        times           : The times when frames were shown, with zero being the first frame time.
    """
    from psychopy import core, event, visual
    from helper_codes.Eye_cup import Eye_cup
    import copy
    import scipy
    import random

    # seed the random number generator
    random.seed(random_seed)

    # make sure the number of dot_sizes an number of speeds is the same:
    while len(dot_sizes) < len(speeds):
        dot_sizes.append(dot_sizes[-1])
    while len(speeds) < len(dot_sizes):
        last_speed = copy.deepcopy(speeds[-1])
        speeds.append(speeds[-1])
        

    # -----------------------------------------
    # -- Set up the window to display things --
    # -----------------------------------------
    draw_rf = False
    # first check to see if a window object was passed
    if window is not None: # a window object WAS passed
        myWin = window # use the passed window
        if eye_cup is None:
            print "No eye cup object passed, reading eye coordinates from file."
            eye_cup = Eye_cup.from_eye_cup_file()
        if rf is not None:
            draw_rf = True
            rf.prep_for_drawing(myWin, shade=shade, opacity=0.07, reverse_rf=True)
            eye_cup.prep_for_drawing(myWin, shade=shade, opacity=0.10, rf=rf)
        else:
            eye_cup.prep_for_drawing(myWin, shade=shade, opacity=0.10)
        frame_info = None # we don't support frame capturing if a window is passed
    else: # a window object WAS NOT passed
        if frame_info is None:
            if rf is None:
                if eye_cup == None:
                    print "No eye cup object passed, reading eye coordinates from file."
                    eye_cup = Eye_cup.from_eye_cup_file()
                # make the new coordinate system based on the eye cup instead of the RF
                from helper_codes.RF import RF
                import copy
                rf = RF([copy.deepcopy(eye_cup.cup[0]), copy.deepcopy(eye_cup.cup[1])],
                        [copy.deepcopy(eye_cup.cup[2]), copy.deepcopy(eye_cup.cup[2])], 0.0)
                myWin = visual.Window( win_size, allowGUI=False, fullscr=False, pos=(0,-900), 
                                rgb=1.0 * shade,
                                viewScale = (2.0*rf.size[0]/float(win_size[0]),2.0*rf.size[1]/float(win_size[1])), 
                                viewPos  = (2.0*rf.center[0]/win_size[0],2.0*rf.center[1]/win_size[1]), 
                                viewOri  = rf.ori )
                eye_cup.prep_for_drawing(myWin, shade=shade, opacity=0.10, rf=rf)
                
            else:
                if eye_cup == None:
                    print "No eye cup object passed, reading eye coordinates from file."
                    eye_cup = Eye_cup.from_eye_cup_file()
                myWin = visual.Window( win_size, allowGUI=False, fullscr=False, pos=(0,-900), 
                                rgb=1.0 * shade,
                                viewScale = (2.0*rf.size[0]/float(win_size[0]),2.0*rf.size[1]/float(win_size[1])), 
                                viewPos  = (2.0*rf.center[0]/win_size[0],2.0*rf.center[1]/win_size[1]), 
                                viewOri  = rf.ori )
                eye_cup.prep_for_drawing(myWin, shade=shade, opacity=0.07, rf=rf)
                rf.prep_for_drawing(myWin, shade=shade, opacity=0.07, reverse_rf=True)
                draw_rf = True
                
        else:
            preview = True
            factor = frame_info['factor']
            win_size = [win_size[0]*factor, win_size[1]*factor]
            myWin = visual.Window( win_size, allowGUI=False, fullscr=False, pos=(0,-900), 
                            rgb=1.0 * shade)

    
    # ---------------------------
    # -- Generate the stimulus --
    # ---------------------------
    # To make a black bar, we first make an all black texture, and place
    #    a circular mask on it.
    texture = scipy.ones((4,4),dtype=scipy.float64)*-1.0*shade
    dots = [ visual.PatchStim( myWin, tex=texture, size = dot_sizes[i], 
             units='norm', mask='circle')\
             for i in xrange(len(dot_sizes)) ]

    # determine the initial positions of all the dots
    positions_list = [ [(1,1)] for i in xrange(len(dots))]
    dot_positions  = [0 for i in xrange(len(dots))]

    # --------------------------------------------------
    # -- Show the Stimulus (or wait and then show it) --
    # --------------------------------------------------


    # -----------------------
    # --  WAIT FOR A PULSE --
    # -----------------------
    if not preview:
        import parallel
        from helper_codes.pport_functions import wait_for_pulse
        p = parallel.Parallel()     # get access to the parallel port
        print "Waiting for pulse..."
        wait_for_pulse(p) # poll the parallel port for a pulse every pport_dt seconds
    else:
        print "Not Waiting for pulse..."

    # start keeping track of time
    trialClock = core.Clock()       # start the clock
    times = [0.0]                   # time begins at 0.0

    keep_looping = True
    while keep_looping:
        if eye_cup != None:
            eye_cup.draw()
        if draw_rf:
            rf.draw()
        # recalculate the trajectory for any dots that need it.
        for i in xrange(len(dots)):
            if dot_positions[i] + 1 == len(positions_list[i]):
                # the final position will be along one or two (if in corner) of the
                #    two walls of the box.  We enumerate the walls from top clockwise around box.
                final_position = positions_list[i][-1]
                if final_position[0] <= -1.0:
                    hit_wall = 3
                if final_position[0] >= 1.0:
                    hit_wall = 1
                if final_position[1] >= 1.0:
                    hit_wall = 0
                if final_position[1] <= -1.0:
                    hit_wall = 2
                new_hit_wall = hit_wall + random.randint(1,3)
                if new_hit_wall > 3:
                    new_hit_wall -= 4
                #print "hit_wall = %d new_hit_wall = %d" % (hit_wall, new_hit_wall)
                if new_hit_wall == 0:
                    new_final_position = ((random.random()-0.5)*2.0, 1.0)
                if new_hit_wall == 1:
                    new_final_position = (1.0, (random.random()-0.5)*2.0)
                if new_hit_wall == 2:
                    new_final_position = ((random.random()-0.5)*2.0, -1.0)
                if new_hit_wall == 3:
                    new_final_position = (-1.0, (random.random()-0.5)*2.0)
                #print "new_final_position = %s" % str(new_final_position)
                #print "getting_new_position_list"
                positions_list[i] = find_x_y(final_position, new_final_position, speeds[i], refresh_rate ) 
                #print positions_list[i]
                dot_positions[i] = 0

            # move the dot to its new position.
            dots[i].setPos( (positions_list[i][dot_positions[i]][0]/2.0,
                             positions_list[i][dot_positions[i]][1]/2.0) )
            dot_positions[i] += 1

            dots[i].draw()
    
        if frame_info != None:
            myWin.getMovieFrame()
    
        # flip the back buffer to the front and the front to the back
        myWin.flip()
        # keep track of when buffers were flipped
        times.append( trialClock.getTime() )

        #handle key presses each frame
        for key in event.getKeys():
            if key in ['escape','q']:
                keep_looping = False

        if frame_info != None:
            if len(times) == len(frame_info['times']):
                keep_looping = False

        if times[-1] >= duration and not preview:
            # if preview = True, then keep looping till esc is hit.
            keep_looping = False
        
        if keep_looping == False:
            break


    # display some information about the frame timings.
    diffs = [(times[i+1] - times[i])*1000 for i in xrange(len(times)-1)]
    print '-------------------------------'
    print '--  TIME BETWEEN FRAMES WAS  --'
    print '-------------------------------'
    for d in diffs[5:10]:
        print "%5.3f" % (d)
    print '--------------------------------'
    print '-- Total time after Pulse was --'
    print '--------------------------------'
    print "%5.3f Seconds" % (times[-1])

    if frame_info != None:
        myWin.saveMovieFrames('frames.gif')
   
    if window == None: # no window object was passed.
        myWin.close()

    return times




if __name__ == '__main__':
    # ---------------------------------
    # -- Set the stimulus parameters --
    # ---------------------------------
    dot_sizes  = [0.16, 0.16, 0.16, 0.22, 0.22]               # in normalized units
    speeds     = [0.50, 0.50, 1.0, 0.25, 0.25]                 # in normalized units
    duration   = 1.0                             # in seconds
    shade      = 1.0                            # 1.0 for white, -1.0 for black background

    # display parameters
    win_size = (1440,900)  # in pixels
    refresh_rate = 60.0    # in Hz
    random_seed = 40

    from helper_codes.RF import RF
    rf = RF.from_rf_file()

    randomly_moving_dots_search(dot_sizes, speeds, duration, shade, 
                         win_size, refresh_rate, random_seed, 
                          preview=True)

